/*
 * scoreMatrix.cpp
 *
 *  Created on: 2011-08-31
 *      Author: pawel
 */
#include "scoreMatrix.h"
#include "smart2DArray.h"
#include<iostream>
#include"PerlComp/ProteicAlphabetPerl.h"
#include<Bpp/Seq/Alphabet/DNA.h>
#include<Bpp/Seq/Alphabet/RNA.h>

#include <EXTERN.h>

using namespace std;

bool  scoreMatrix::isEntropyValid()const{return EntropyValid;}
bool  scoreMatrix::isExpectedScoreValid()const{return ExpectedScoreValid;}
bool  scoreMatrix::isScaleValid()const{return ScaleValid; }
bool  scoreMatrix::isHighestScoreValid()const{return HighestScoreValid;}
bool  scoreMatrix::isLowestScoreValid()const{return LowestScoreValid;}
bool  scoreMatrix::isLambdaValid()const{return LambdaValid;}
bool  scoreMatrix::isHValid()const{return HValid;}


double scoreMatrix::getEntropy()const{return entropy;}
double scoreMatrix::getExpectedScore()const{return expectedScore;}
double scoreMatrix::getScale()const{return scale;}
char scoreMatrix::getHighestScore()const{return highestScore;}
char scoreMatrix::getLowestScore()const{return lowestScore;}
double scoreMatrix::getLambda()const{return lambda;}
double scoreMatrix::getH()const{return H;}
extern "C" {
#include <perl.h>
}

EXTERN_C void xs_init (pTHX);
EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);

static  PerlInterpreter *my_perl;
//TODO Na razie biblioteki linkowane manualnie
/*
 * Dynamic loader of perl modules
 */
EXTERN_C void xs_init(pTHX)
{
	char *file = __FILE__;
	dXSUB_SYS;
	/* DynaLoader is a special case */
	newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);

}
scoreMatrix::scoreMatrix(const string filename, const AlphabetType type,bool fake)
:matrix(NULL),size(24),EntropyValid(false),entropy(0.0),ExpectedScoreValid(false),expectedScore(0.0),
ScaleValid(false),scale(0.0),HighestScoreValid(false),highestScore(0),LowestScoreValid(false),
lowestScore(0),LambdaValid(false),lambda(0.0),HValid(false),H(0.0)
{
	switch(type){
	case AA:
	{
		std::auto_ptr<ProteicAlphabetPerl> smartAlphabet(new ProteicAlphabetPerl);
		alphabet = smartAlphabet.release();
	}
			break;
	case DNA:
	{
		std::auto_ptr<bpp::DNA> smartAlphabet(new bpp::DNA);
		alphabet = smartAlphabet.release();
	}break;
	case RNA:
	{
		std::auto_ptr<bpp::RNA> smartAlphabet(new bpp::RNA);
		alphabet = smartAlphabet.release();
	}break;
	default:
			throw std::logic_error("Unknown alphabet type");
			break;
	}

	try{
	smart2DArray<char> smartArray(size,size);
	matrix = smartArray.getPtr();
	smartArray.release();
	}catch(std::bad_alloc& e){
		throw;
	}
	for(size_t i=0;i<size;i++){
		memset(matrix[i],1,size*sizeof(char));
	}
	colnames.insert(std::pair<char,int>('A',0));
	colnames.insert(std::pair<char,int>('R',1));
	colnames.insert(std::pair<char,int>('N',2));
	colnames.insert(std::pair<char,int>('D',3));
	colnames.insert(std::pair<char,int>('C',4));
	colnames.insert(std::pair<char,int>('Q',5));
	colnames.insert(std::pair<char,int>('E',6));
	colnames.insert(std::pair<char,int>('G',7));
	colnames.insert(std::pair<char,int>('H',8));
	colnames.insert(std::pair<char,int>('I',9));
	colnames.insert(std::pair<char,int>('L',10));
	colnames.insert(std::pair<char,int>('K',11));
	colnames.insert(std::pair<char,int>('M',12));
	colnames.insert(std::pair<char,int>('F',13));
	colnames.insert(std::pair<char,int>('P',14));
	colnames.insert(std::pair<char,int>('S',15));
	colnames.insert(std::pair<char,int>('T',16));
	colnames.insert(std::pair<char,int>('W',17));
	colnames.insert(std::pair<char,int>('Y',18));
	colnames.insert(std::pair<char,int>('V',19));
	colnames.insert(std::pair<char,int>('B',20));
	colnames.insert(std::pair<char,int>('Z',21));
	colnames.insert(std::pair<char,int>('X',22));
	colnames.insert(std::pair<char,int>('*',23));
	rownames = colnames;


}
scoreMatrix::scoreMatrix(const string filename,const AlphabetType type)
:matrix(NULL),size(0),EntropyValid(false),entropy(0.0),ExpectedScoreValid(false),expectedScore(0.0),
ScaleValid(false),scale(0.0),HighestScoreValid(false),highestScore(0),LowestScoreValid(false),
lowestScore(0),LambdaValid(false),lambda(0.0),HValid(false),H(0.0)
{
	switch(type){
	case AA:
	{
		std::auto_ptr<ProteicAlphabetPerl> smartAlphabet(new ProteicAlphabetPerl);
		alphabet = smartAlphabet.release();
	}
			break;
	case DNA:
	{
		std::auto_ptr<bpp::DNA> smartAlphabet(new bpp::DNA);
		alphabet = smartAlphabet.release();
	}break;
	case RNA:
	{
		std::auto_ptr<bpp::RNA> smartAlphabet(new bpp::RNA);
		alphabet = smartAlphabet.release();
	}break;
	default:
			throw std::logic_error("Unknown alphabet type");
			break;
	}
	alphabetSymbolsTypes = alphabet->getNumberOfTypes();
	char *my_argv[] = { "", "./scoringMatrix.pl" };

	      my_perl = perl_alloc();
	      perl_construct( my_perl );

	      perl_parse(my_perl, xs_init, 2, my_argv, (char **)NULL);
	      //perl_run(my_perl);
	      try{
	      loadMatrix(filename,type);
	      getSize();

	      smart2DArray<char> smartArray(size,size);

	      for(size_t i=0;i<size;i++){
				 fillRow(i,(smartArray.getPtr())[i]);
	      }
	      getRowNames();
	      getColNames();
	      loadEntropy();
	      loadScale();
	      loadExpectedScore();
	      loadHighestScore();
	      loadLowestScore();
	      loadLambda();
	      loadH();
	      matrix = smartArray.getPtr();
	      smartArray.release();
	      }catch(std::exception & e){
	    	  perl_destruct(my_perl);
	    	  perl_free(my_perl);
	    	  throw;
	      }
	      perl_destruct(my_perl);
	      perl_free(my_perl);
}
scoreMatrix::~scoreMatrix(){
	if(matrix != NULL){
	for(size_t i =0 ;i< size;i++){
		delete[] matrix[i];
	}
	delete[] matrix;
	}
	delete alphabet;

}
int scoreMatrix::loadH(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getH", G_ARRAY|G_NOARGS);
	SPAGAIN;
	HValid = (bool)POPi;
	H = POPn;
	PUTBACK;
	FREETMPS;
	LEAVE;

	return 0;
}
int scoreMatrix::loadLambda(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getLambda", G_ARRAY|G_NOARGS);
	SPAGAIN;
	LambdaValid = (bool)POPi;
	lambda = POPn;
	PUTBACK;
	FREETMPS;
	LEAVE;
//
	return 0;
}
int scoreMatrix::loadLowestScore(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getLowestScore", G_ARRAY|G_NOARGS);
	SPAGAIN;
	LowestScoreValid = (bool)POPi;
	lowestScore = (char)POPi;
	PUTBACK;
	FREETMPS;
	LEAVE;

	return 0;
}
int scoreMatrix::loadHighestScore(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getHighestScore", G_ARRAY|G_NOARGS);
	SPAGAIN;
	HighestScoreValid = (bool)POPi;
	highestScore = (char)POPi;
	PUTBACK;
	FREETMPS;
	LEAVE;
//
	return 0;
}
int scoreMatrix::loadExpectedScore(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getExpectedScore", G_ARRAY|G_NOARGS);
	SPAGAIN;
	ExpectedScoreValid = (bool)POPi;
	expectedScore= POPn;
	PUTBACK;
	FREETMPS;
	LEAVE;

	return 0;
}
int scoreMatrix::loadScale(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getScale", G_ARRAY|G_NOARGS);
	SPAGAIN;
	ScaleValid = (bool)POPi;
	scale = POPn;
	PUTBACK;
	FREETMPS;
	LEAVE;

	return 0;
}
int scoreMatrix::loadEntropy(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getEntropy", G_ARRAY|G_NOARGS);
	SPAGAIN;
	EntropyValid = (bool)POPi;
	entropy = POPn;
	PUTBACK;
	FREETMPS;
	LEAVE;

	return 0;
}
char scoreMatrix::getScore(int a,int b){
	return matrix[(size_t)a][(size_t)b];
}
char** scoreMatrix::getMatrix(){
	return matrix;
}
int scoreMatrix::getColNames(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getColNames", G_ARRAY);
	SPAGAIN;
	int pop;
	for(int i=size-1;i>=0;i--){
		pop = POPi;
		colnames.insert(std::pair<char,int>((char)pop,i));
		std::string str((char*)(&pop),1);
		if(alphabet->charToInt(str) != i){
			throw std::logic_error("Invalid cols order");
		}
	}
	PUTBACK;
	FREETMPS;
	LEAVE;

return 0;
}
int scoreMatrix::getRowNames(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getRowNames", G_ARRAY);
	SPAGAIN;
	int pop;

	for(int i=size-1;i>=0;i--){
		pop = POPi;
		rownames.insert(std::pair<char,int>((char)pop,i));
		std::string str((char*)(&pop),1);
		if(alphabet->charToInt(str) != i){
			throw std::logic_error("Invalid rows order");
		}
	}


	PUTBACK;
	FREETMPS;
	LEAVE;

return 0;
}
int scoreMatrix::fillRow(size_t row,char* rowptr){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	XPUSHs(sv_2mortal(newSViv((int)row)));
	PUTBACK;
	call_pv("getRow", G_ARRAY);
	SPAGAIN;
	for(int i=size-1;i>=0;i--){
		rowptr[i]=(char)POPi;
	}
	PUTBACK;
	FREETMPS;
	LEAVE;

return 0;
}
int scoreMatrix::getSize(){
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	PUTBACK;
	call_pv("getSize", G_SCALAR|G_NOARGS);
	SPAGAIN;
	size = (size_t)(POPi);
	PUTBACK;
	FREETMPS;
	LEAVE;
	return 0;
}
int scoreMatrix::loadMatrix(string filename,AlphabetType typeA)throw(std::logic_error){
	std::string type;
	switch(typeA){
	case AA:
		type="prot";
		break;
	case DNA:
	case RNA:
		type="nuc";
		break;
	default:
		throw std::logic_error("Unknown alphabet type");
		break;
	}
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	XPUSHs(sv_2mortal(newSVpv((char*)(filename.c_str()), 0)));
	XPUSHs(sv_2mortal(newSVpv((char*)(type.c_str()), 0)));
	PUTBACK;
	call_pv("loadScoringMatrix", G_EVAL|G_DISCARD);
	try{
	 if (SvTRUE(ERRSV)){
		 throw(std::logic_error(SvPV_nolen(ERRSV)));
	 }
	}catch(std::logic_error& e){
		FREETMPS;
		LEAVE;
		throw;
	}
	FREETMPS;
	LEAVE;
	return 0;
}

